/**
 * AntColony
 *
 * It's a very simple implementation of path finder algorithm using the "ant colony" technique.
 */

/**
 * Ant Colony algorithm that uses the game Map object
 *
 * @param {Map} map The map object
 * @constructor
 */
var AntColony = exports.AntColony = function(map) {
	this.map = map;
	this.size = map.getSize();
	this.phMap = new Int8Array(this.size[0] * this.size[1]);

	this.initialRefresh = 1000;
	this.refresh = this.initialRefresh;
	this.initialPh = 800;
	this.phDecrementPerSecond = 60;
}

/**
 * It is used to find if there's a pheromone track near the center of the object
 *
 * @param {[number, number]} center the center of the object [x,y].
 *
 * @return {[number,number]} max pheromone level found (up and down the object) [down, up].
 * @private
 */
AntColony.prototype.getProb = function(center) {
	var maxPh1 = 0;
	for (var i = 10; i < 70; i = i + 3) {
		if (maxPh1 < this.getPh([center[0], center[1] + i])) {
			maxPh1 = this.getPh([center[0], center[1] + i]);
		}
	}

	var maxPh2 = 0;
	for (var i = 10; i < 70; i = i + 3) {
		if (maxPh2 < this.getPh([center[0], center[1] - i])) {
			maxPh2 = this.getPh([center[0], center[1] - i]);
		}
	}

	return [maxPh1, maxPh2];
};

/**
 * It modifies the cell's pheromone level.
 *
 * @param {[number, number]} pos the cell [x,y] where the pheromone level must be modified.
 * @param {number} value the new pheromone level.
 *
 * @private
 */
AntColony.prototype.setPh = function(pos, value) {
	var width = this.size[0];
	for (var i = -1; i <= 1; i++) {
		//if (this.phMap[pos[0] + (pos[1] + i) * width] === 0) {
			this.phMap[pos[0] + (pos[1] + i) * width] = value;
		//}
		//else {
		//	this.phMap[pos[0] + (pos[1] + i) * width] += 20 ;
		//}
	}
};

/**
 * It is used to read the pheromone level of a cell
 *
 * @param {[number, number]} pos the cell [x,y] we want to know the level pheromon level.
 *
 * @return {number} the cell's pheromone level.
 * @private
 */
AntColony.prototype.getPh = function(pos) {
	var width = this.size[0];

	if (this.phMap[pos[0] + pos[1] * width] === 0) {
		return 1;
	}
	else {
		return this.phMap[pos[0] + pos[1] * width];
	}
};

/**
 * It gives the next cell where the object has to move
 *
 * @param {[number, number]} center the current cell [x,y] where the object is.
 * @param {[number, number]} dim the dimensions of the object [dim_x, dim_y] (must be positive).
 *
 * @return {[number, number]} the next cell [x,y] where the object should go.
 * @private
 */
AntColony.prototype.next = function(center, dim) {
	//generating the points to control
	var position = [
		[center[0] - dim[0] / 2, center[1]],
		[center[0] - dim[0] / 2, center[1] + dim[1] / 2],
		[center[0], center[1] + dim[1] / 2],
		[center[0] - dim[0] / 2, center[1] - dim[1] / 2],
		[center[0] + dim[0] / 2, center[1] + dim[1] / 2],
		[center[0], center[1] - dim[1] / 2],
		[center[0] + dim[0] / 2, center[1] - dim[1] / 2],
		[center[0] + dim[0] / 2, center[1]]
	];

	// the costs of each direction
	var cost = [10, 7, 7, 1];

	var auxPath = [];
	for (var i = 0; i <= 7; i++) {
		auxPath[i] = !( this.map.isFilled(position[i]));
	}

	//get the possible directions inside the map
	var path = [];
	path[0] = (auxPath[1] || auxPath[3]) || (position[0][0]<=0);
	path[1] = auxPath[2] && auxPath[1];
	path[2] = auxPath[5] && auxPath[3];
	path[3] = auxPath[4] && auxPath[6] && auxPath[7];

	//get the ph. level
	var ph = [];
	ph[0] = this.getPh([center[0]+1, center[1]]);
	ph[1] = this.getPh([center[0], center[1] + 1]);
	ph[2] = this.getPh([center[0], center[1] - 1]);
	//in this moment enemy can't go back
	ph[3] = 1;

	//find the most convenient direction
	var max = 0;
	for (var i = 1; i <= 3; i++) {
		if (path[i] * ph[i] * cost[i] > path[max] * ph[max] * cost[max]) {
			max = i;
		}
	}

	//get the cost of each side
	var ck = []
	for (var i = 0; i <= 3; i++) {
		ck[i] = ph[i] * path[i];
	}


	//control if the object can move freely
	if( (path[0]) &&  (path[1]) &&  (path[2])){
		var prob = this.getProb([center[0] - 1, center[1]]);
		//the object can move from a pheromone track to a stronger one
		if ((ph[max] < prob[1]) || (ph[max] < prob[0])) {
			// check which side is the most convenient (up and down)
			if (prob[0] > prob[1]) {
				max = 4;
			}
			else {
				max = 5;
			}
		}
		else{
			//object moves randomly to find a pheromone track or the end of the map
			max=Math.floor((Math.random()*2) + 4);
		}

	}
	else{
		//object can't go on
		if(!(path[0])){
			//watch what side is the most convenient (locally)
			if(ck[1] != ck[2]){
				if(ck[1] > ck[2]){
					max = 1;
				}
				else{
					max = 2;
				}
			}
			else{
				var prob = this.getProb([center[0] - 1, center[1]]);
				//the object can move from a pheromone track to a stronger one
				if ((ph[max] < prob[1]) || (ph[max] < prob[0])) {
					// check which side is the most convenient (up and down)
					if (prob[0] > prob[1]) {
						max = 1;
					}
					else {
						max = 2;
					}
				}
				else{
					//in this case object dosen't have enough elements to decide
					max=Math.floor((Math.random()*2)+6);
				}
			}

		};

		//object can't move down
		if(! path[1]){
			//object can move up
			if(path[2]){
				//?object can go on
				if(path[0]){
					max = 5;
				}
				else{
					max = 2;
				}
			};
		};

		//object can't move up
		if(! path[2]){
			//object can move down
			if(path[1]){
				//?object can go on
				if(path[0]){
					max = 4;
				}
				else{
					max = 1;
				}
			};
		};


	}

	switch (max) {
		case 0:
			return [center[0] - 1, center[1]];
			break;
		case 1:
			return [center[0], center[1] + 1];
			break;
		case 2:
			return [center[0], center[1] - 1];
			break;
		case 3:
			return [center[0] + 1 , center[1]];
			break;
		case 4:
			return [center[0] - 1, center[1] + 1];
			break;
		case 5:
			return [center[0] - 1, center[1] - 1];
			break;
		case 6:
			return [center[0] + 1, center[1] + 1];
			break;
		case 7:
			return [center[0] + 1, center[1] - 1];
			break;
		default:
			return center;
	}
};


/**
 * It creates a new pheromone track, call this method when the object reach the end of the map
 *
 * @param {Array of coordinates[x,y]} path the path of the object (must have 2 or more elements)
 */
AntColony.prototype.done = function(path) {
	for (var i = 1; i < path.length; i++) {
		if ((path[i][1] != path[i - 1][1]) || (path[i][0] != path[i - 1][0])) {
			// we set the initial strength to 800
			this.setPh(path[i], this.initialPh);
		}
	}
};

/**
 * It simulates at the pheromone evaporation.
 *
 * @param {number} msDuration The milliseconds since the previous update
 */
AntColony.prototype.update = function(msDuration) {
	this.refresh -= msDuration;

	if (this.refresh < 0) { //for CPU optimization
		for (var i = 0, len = this.phMap.length; i < len; i++) {
			if (this.phMap[i] - this.phDecrementPerSecond <= 1) {
				this.phMap[i] = 0;
			}
			else {
				this.phMap[i] -= this.phDecrementPerSecond;
			}
		}

		this.refresh = this.initialRefresh;
	}
};